// ViewSettings.h

#include "StdAfx.h"

#include "Common/String/StringConvert.h"

#include "ViewSettings.h"
#include "Common/Registry/Registry.h"

#include "App.h"

using namespace Common;
using namespace System;

#define REG_PATH_FM _T("Software") STRING_PATH_SEPARATOR _T("7-Zip") STRING_PATH_SEPARATOR _T("FM")

static const TCHAR* kCUBasePath = REG_PATH_FM;
static const TCHAR* kCulumnsKeyName = REG_PATH_FM STRING_PATH_SEPARATOR _T("Columns");

static const TCHAR* kPositionValueName = _T("Position");
static const TCHAR* kPanelsInfoValueName = _T("Panels");
static const TCHAR* kToolbars = _T("Toolbars");

static const TCHAR* kPanelPathValueName = _T("PanelPath");
static const TCHAR* kListMode = _T("ListMode");
static const TCHAR* kFolderHistoryValueName = _T("FolderHistory");
static const TCHAR* kFastFoldersValueName = _T("FolderShortcuts");
static const TCHAR* kCopyHistoryValueName = _T("CopyHistory");


static const UINT32 kColumnInfoSpecHeader = 12;
static const UINT32 kColumnHeaderSize = 12;

static const UINT32 kColumnInfoVersion = 1;

static CComAutoCriticalSection g_CS;

class CTempOutBufferSpec
{
	Common::Registry::CKey::ByteBuffer Buffer;
	UINT32 Size;
	UINT32 Pos;
public:
	operator const BYTE* () const
	{
		if (0 == Buffer.size())
		{
			return NULL;
		}
		return (const BYTE*)&Buffer[0];
	}
	void Init(UINT32 dataSize)
	{
		//Buffer.SetCapacity(dataSize);
		Buffer.resize(dataSize);
		Size = dataSize;
		Pos = 0;
	}
	void WriteByte(BYTE value)
	{
		if (Pos >= Size)
		{
			throw "overflow";
		}
		Buffer[Pos++] = value;
	}
	void WriteUInt32(UINT32 value)
	{
		for (int i = 0; i < 4; i++)
		{
			WriteByte((BYTE)value);
			value >>= 8;
		}
	}
	void WriteBool(bool value)
	{
		WriteUInt32(value ? 1 : 0);
	}
};

class CTempInBufferSpec
{
public:
	BYTE* Buffer;
	UINT32 Size;
	UINT32 Pos;
	BYTE ReadByte()
	{
		if (Pos >= Size)
		{
			throw "overflow";
		}
		return Buffer[Pos++];
	}
	UINT32 ReadUInt32()
	{
		UINT32 value = 0;
		for (int i = 0; i < 4; i++)
		{
			value |= (((UINT32)ReadByte()) << (8 * i));
		}
		return value;
	}
	bool ReadBool()
	{
		return (ReadUInt32() != 0);
	}
};

void SaveListViewInfo(const CString &id, const CListViewInfo &viewInfo)
{
	const std::vector<CColumnInfo> &columns = viewInfo.Columns;
	CTempOutBufferSpec buffer;
	UINT32 dataSize = kColumnHeaderSize + kColumnInfoSpecHeader * columns.size();
	buffer.Init(dataSize);

	buffer.WriteUInt32(kColumnInfoVersion);
	buffer.WriteUInt32(viewInfo.SortID);
	buffer.WriteBool(viewInfo.Ascending);
	for(size_t i = 0; i < columns.size(); i++)
	{
		const CColumnInfo &column = columns[i];
		buffer.WriteUInt32(column.PropID);
		buffer.WriteBool(column.IsVisible);
		buffer.WriteUInt32(column.Width);
	}
	{
		Threading::CComScopeAutoLock lock(g_CS);
		Common::Registry::CKey key;
		key.Create(HKEY_CURRENT_USER, kCulumnsKeyName);
		key.SetValue(String::GetSystemString(id), (const BYTE*)buffer, dataSize);
	}
}

void ReadListViewInfo(const CString &id, CListViewInfo &viewInfo)
{
	viewInfo.Clear();
	std::vector<CColumnInfo> &columns = viewInfo.Columns;
	Common::Registry::CKey::ByteBuffer buffer;
	UINT32 size;
	{
		Threading::CComScopeAutoLock lock(g_CS);
		Common::Registry::CKey key;
		if (key.Open(HKEY_CURRENT_USER, kCulumnsKeyName, KEY_READ) != ERROR_SUCCESS)
		{
			return;
		}
		if (key.QueryValue(String::GetSystemString(id), buffer, size) != ERROR_SUCCESS)
		{
			return;
		}
	}
	if (size < kColumnHeaderSize)
	{
		return;
	}
	CTempInBufferSpec inBuffer;
	inBuffer.Size = size;
	if (buffer.size())
	{
		inBuffer.Buffer = (BYTE*)&buffer[0];
	}

	inBuffer.Pos = 0;


	UINT32 version = inBuffer.ReadUInt32();
	if (version != kColumnInfoVersion)
	{
		return;
	}
	viewInfo.SortID = inBuffer.ReadUInt32();
	viewInfo.Ascending = inBuffer.ReadBool();

	size -= kColumnHeaderSize;
	if (size % kColumnInfoSpecHeader != 0)
	{
		return;
	}
	int numItems = size / kColumnInfoSpecHeader;
	columns.reserve(numItems);
	for(int i = 0; i < numItems; i++)
	{
		CColumnInfo columnInfo;
		columnInfo.PropID = inBuffer.ReadUInt32();
		columnInfo.IsVisible = inBuffer.ReadBool();
		columnInfo.Width = inBuffer.ReadUInt32();
		columns.push_back(columnInfo);
	}
}

static const UINT32 kWindowPositionHeaderSize = 5 * 4;
static const UINT32 kPanelsInfoHeaderSize = 3 * 4;

void SaveWindowSize(const RECT &rect, bool maximized)
{
	CString keyName = kCUBasePath;
	Threading::CComScopeAutoLock lock(g_CS);
	Common::Registry::CKey key;
	key.Create(HKEY_CURRENT_USER, keyName);
	CTempOutBufferSpec buffer;
	buffer.Init(kWindowPositionHeaderSize);
	buffer.WriteUInt32(rect.left);
	buffer.WriteUInt32(rect.top);
	buffer.WriteUInt32(rect.right);
	buffer.WriteUInt32(rect.bottom);
	buffer.WriteBool(maximized);
	key.SetValue(kPositionValueName, (const BYTE*)buffer, kWindowPositionHeaderSize);
}

bool ReadWindowSize(RECT &rect, bool &maximized)
{
	CString keyName = kCUBasePath;
	Threading::CComScopeAutoLock lock(g_CS);
	Common::Registry::CKey key;
	if (key.Open(HKEY_CURRENT_USER, keyName, KEY_READ) != ERROR_SUCCESS)
	{
		return false;
	}
	Common::Registry::CKey::ByteBuffer buffer;
	UINT32 size;
	if (key.QueryValue(kPositionValueName, buffer, size) != ERROR_SUCCESS)
	{
		return false;
	}
	if (size != kWindowPositionHeaderSize)
	{
		return false;
	}
	CTempInBufferSpec inBuffer;
	inBuffer.Size = size;
	if (buffer.size())
	{
		inBuffer.Buffer = (BYTE*)&buffer[0];
	}
	inBuffer.Pos = 0;
	rect.left = inBuffer.ReadUInt32();
	rect.top = inBuffer.ReadUInt32();
	rect.right = inBuffer.ReadUInt32();
	rect.bottom = inBuffer.ReadUInt32();
	maximized = inBuffer.ReadBool();
	return true;
}

void SavePanelsInfo(UINT32 numPanels, UINT32 currentPanel, UINT32 splitterPos)
{
	CString keyName = kCUBasePath;
	Threading::CComScopeAutoLock lock(g_CS);
	Common::Registry::CKey key;
	key.Create(HKEY_CURRENT_USER, keyName);

	CTempOutBufferSpec buffer;
	buffer.Init(kPanelsInfoHeaderSize);
	buffer.WriteUInt32(numPanels);
	buffer.WriteUInt32(currentPanel);
	buffer.WriteUInt32(splitterPos);
	key.SetValue(kPanelsInfoValueName, (const BYTE*)buffer, kPanelsInfoHeaderSize);
}

void SaveToolbarsMask(UINT32 toolbarMask)
{
	Common::Registry::CKey key;
	key.Create(HKEY_CURRENT_USER, kCUBasePath);
	key.SetValue(kToolbars, toolbarMask);
}

static const UINT32 kDefaultToolbarMask = ((UINT32)1 << 31) | 8 | 4 | 1;

UINT32 ReadToolbarsMask()
{
	Common::Registry::CKey key;
	if (key.Open(HKEY_CURRENT_USER, kCUBasePath, KEY_READ) != ERROR_SUCCESS)
	{
		return kDefaultToolbarMask;
	}
	UINT32 mask;
	if (key.QueryValue(kToolbars, mask) != ERROR_SUCCESS)
	{
		return kDefaultToolbarMask;
	}
	return mask;
}


static CString GetPanelPathName()
{
	return CString(kPanelPathValueName);
}


void SavePanelPath(const CString &path)
{
	Threading::CComScopeAutoLock lock(g_CS);
	Common::Registry::CKey key;
	key.Create(HKEY_CURRENT_USER, kCUBasePath);
	key.SetValue(GetPanelPathName(), path);
}

bool ReadPanelPath(CString &path)
{
	Threading::CComScopeAutoLock lock(g_CS);
	Common::Registry::CKey key;
	if (key.Open(HKEY_CURRENT_USER, kCUBasePath, KEY_READ) != ERROR_SUCCESS)
	{
		return false;
	}
	return (key.QueryValue(GetPanelPathName(), path) == ERROR_SUCCESS);
}

void SaveListMode(const CListMode &listMode)
{
	Common::Registry::CKey key;
	key.Create(HKEY_CURRENT_USER, kCUBasePath);
	UINT32 t = 0;
	t |= ((listMode.Panel) & 0xFF) << 8;

	key.SetValue(kListMode, t);
}

void ReadListMode(CListMode &listMode)
{
	Common::Registry::CKey key;
	listMode.Init();
	if (key.Open(HKEY_CURRENT_USER, kCUBasePath, KEY_READ) != ERROR_SUCCESS)
	{
		return;
	}
	UINT32 t;
	if (key.QueryValue(kListMode, t) != ERROR_SUCCESS)
	{
		return;
	}
	listMode.Panel = (t & 0xFF);
	t >>= 8;
}


static void SaveStringList(LPCTSTR valueName, const CStringVector &folders)
{
	Threading::CComScopeAutoLock lock(g_CS);
	Common::Registry::CKey key;
	key.Create(HKEY_CURRENT_USER, kCUBasePath);
	key.SetValue_Strings(valueName, folders);
}

static void ReadStringList(LPCTSTR valueName, CStringVector &folders)
{
	folders.clear();
	Threading::CComScopeAutoLock lock(g_CS);
	Common::Registry::CKey key;
	if (key.Open(HKEY_CURRENT_USER, kCUBasePath, KEY_READ) == ERROR_SUCCESS)
	{
		key.GetValue_Strings(valueName, folders);
	}
}

void SaveFolderHistory(const CStringVector &folders)
{
	SaveStringList(kFolderHistoryValueName, folders);
}
void ReadFolderHistory(CStringVector &folders)
{
	ReadStringList(kFolderHistoryValueName, folders);
}

void SaveFastFolders(const CStringVector &folders)
{
	SaveStringList(kFastFoldersValueName, folders);
}
void ReadFastFolders(CStringVector &folders)
{
	ReadStringList(kFastFoldersValueName, folders);
}

void SaveCopyHistory(const CStringVector &folders)
{
	SaveStringList(kCopyHistoryValueName, folders);
}
void ReadCopyHistory(CStringVector &folders)
{
	ReadStringList(kCopyHistoryValueName, folders);
}

void AddUniqueStringToHeadOfList(CStringVector &list, const CString &s)
{
	for (size_t i = 0; i < list.size();)
		if (s.CompareNoCase(list[i]) == 0)
		{
			list.erase(list.begin() + i);
		}
		else
		{
			i++;
		}
	list.push_front(s);
}
